/******************************************************************
 * Created by: GOH CHEE HONG                                      
                                          
 * Date of last modification: 06/SEP/2008
 *                                                                
 * Description: Provide means to control network communication 
 * of the program
 * 
 * Status: Optimized and Finished
 ******************************************************************/

/******************************************************************
 * Created by: DO HOANG HAI                                       
 * Date of Creation: 10/Oct/2007                                               
 * Date of last modification: 22/02/2008
 *                                                                
 * Description: Provide means to control network communication 
 * of the program
 * 
 * Status: Optimized and Finished
 ******************************************************************/
using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.Net.Sockets;
using Org.BouncyCastle.Bcpg.OpenPgp;
using Org.BouncyCastle.Bcpg;
using System.Collections;
using Pbdms.Common.CryptoUtils;
using Pbdms.Common.SharedVariables;
using System.IO;
using Pbdms.Common.DataObject;
using Pbdms.NetworkDealer;
using System.Threading;
using Pbdms.BusinessLogic.InstantMessaging;
using Pbdms.BusinessLogic.FileTransfer;
using System.Runtime.Serialization.Formatters.Binary;
namespace Pbdms.BusinessLogic.Network
{
    /// <summary>
    /// a NetworkController object controls all the interactions with the network 
    /// and handles events from networkDealer module (data received from the network
    /// but can't be processed by this module will be passed to this NetworkController object)
    /// 
    /// </summary>
    public class NetworkController
    {
        /// <summary>
        /// Maximum hops for the request to be sent around the network
        /// </summary>
        public const int MAX_HOPS = 5;
        public FriendsControl friendsControl;
        public SecretKeyControl keyControl;
        public PublicKeysControl publicKeyControl;
        /// <summary>
        /// a Detwork Dealer component attached to the object to provide networking services
        /// </summary>
        public NetworkDealer.NetworkDealer ntwkDealer = null;

        /// <summary>
        /// a reference to the method which process the search results received from the network
        /// </summary>
        public SearchResultReceiver processSearchResultReceived;
        




       

      

        /// <summary>
        /// a delegate that defines SearchResultReceived event handler
        /// </summary>
        /// <param name="o1"></param>
        /// <param name="o2"></param>
        public delegate void SearchResultReceiver(object sender, Pbdms.NetworkDealer.EventArgs.SearchResultReceivedEventArgs e);
       
      

       
  



        #region new event handler
        public delegate void DownloadRequestHandler(object sender, NetworkMessage message);
        public event DownloadRequestHandler DownloadRequest;
        public delegate void VerifyKeyResponseHandler(object sender, NetworkMessage message);
        public event VerifyKeyResponseHandler VerifyKeyResponse;
        public delegate void InstantMessageReceivedHandler(object sender, NetworkMessage message);
        public event InstantMessageReceivedHandler InstantMessageReceived;
        public delegate void FileTransferRequestReceivedHandler(object sender, NetworkMessage message);
        public event FileTransferRequestReceivedHandler FileTransferRequestReceived;

        public delegate void BrowseResultBuddyReceivedHandler(object sender,ResultItem result);
        public event BrowseResultBuddyReceivedHandler BrowseResultRequestReceived;
       
        #endregion
        /// <summary>
        /// send a search request to the network
        /// </summary>
        /// <param name="searchObj"></param>
        /// <param name="processSearchResultReceived"></param>
        public void sendSearchRequest(Common.DataObject.SearchObj searchObj, SearchResultReceiver processSearchResultReceived)
        {
            this.processSearchResultReceived = processSearchResultReceived;
            
            try
            {

                //form the SearchRequest object
                NetworkDealer.Requests.SearchRequest srq = new NetworkDealer.Requests.SearchRequest();
                srq.curHop = 0;
                srq.ipAddForwarder = null;
                srq.ipAddSource = NetworkDealer.NetworkDealer.ipAddMyComputer.ToString();
                srq.requestId = ntwkDealer.getRequestId();
                srq.searchObj = searchObj;

                //serialize the search object to xml string
                System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(srq.GetType());
                System.IO.StringWriter str = new System.IO.StringWriter();
                serializer.Serialize(str, srq);

                NetworkMessage message = new NetworkMessage();
                message.TypeOfMessage = NetworkDealer.NetworkDealer.SEARCH_REQUEST;
                message.MessageContent = str.ToString();

                ArrayList arlBuddies = friendsControl.getAuthenticatedFriendList();
                //send the request to all buddies
                if (arlBuddies != null)
                {
           
                    foreach (Common.DataObject.Buddy bdd in arlBuddies)
                    {
                        //if (!bdd.IpAdd.Equals("") && !bdd.IpAdd.Equals(NetworkDealer.NetworkDealer.ipAddMyComputer.ToString()))
                        {
                            AuthTcpClient authTcpClient = new AuthTcpClient(bdd.IpAdd, NetworkDealer.NetworkDealer.LISTEN_PORT, bdd.KeyID, keyControl, publicKeyControl, friendsControl);
                            try
                            {
                                authTcpClient.Connect();
                                if (authTcpClient.IsAuthenticated() == false)
                                {
                                    throw new Exception("error");
                                }
                                authTcpClient.sendMessage(message);
                            }
                            catch
                            {

                            }
                            finally
                            {
                                if (authTcpClient != null)
                                {
                                    authTcpClient.Close();
                                }
                            }
                        }
                    }
                }
            }
            catch
            {
                Common.Global.showMsgOneButton("An unexpected error has occurred, the program is unable to send the request.\r\nPlease try again latter!", 'W');
            }
            
            //wait for 7 seconds for the result from the network
            System.Threading.Thread.CurrentThread.Join(10000);
        }

   

      

        /// <summary>
        /// Cnstructor
        /// </summary>
        public NetworkController(FriendsControl friendsControl, SecretKeyControl secretKeyControl, PublicKeysControl publicKeyControl)
        {
            ntwkDealer = new Pbdms.NetworkDealer.NetworkDealer(secretKeyControl, publicKeyControl,friendsControl);
            
            ntwkDealer.SearchRequestReceived +=new Pbdms.NetworkDealer.NetworkDealer.SearchRequestReceivedEventHandler(ntwkDealer_SearchRequestReceived); 
            ntwkDealer.searchResultReceived +=new Pbdms.NetworkDealer.NetworkDealer.SearchResultReceivedEventHandler(ntwkDealer_searchResultReceived);

            ntwkDealer.browseResultBuddyReceived+= new Pbdms.NetworkDealer.NetworkDealer.BrowseResultBuddyReceivedEventHandler(ntwkDealer_browseResultBuddyReceived);
            ntwkDealer.browseRequestReceived += new Pbdms.NetworkDealer.NetworkDealer.BrowseRequestReceivedHandler(ntwkDealer_browseRequestReceived);
    

            //new event
            ntwkDealer.DownloadRequest += new Pbdms.NetworkDealer.NetworkDealer.DownloadRequestHandler(ntwkDealer_DownloadRequest);
            ntwkDealer.FeedRequestReceived += new Pbdms.NetworkDealer.NetworkDealer.FeedRequestReceivedHandler(ntwkDealer_FeedRequestReceived);
            ntwkDealer.ViewAnItemRequest += new Pbdms.NetworkDealer.NetworkDealer.ViewAnItemRequestHandler(ntwkDealer_ViewAnItemRequest);
            ntwkDealer.InstantMessageReceived += new Pbdms.NetworkDealer.NetworkDealer.InstantMessageReceivedHandler(ntwkDealer_InstantMessageReceived);
            ntwkDealer.FileTransferRequestReceived += new Pbdms.NetworkDealer.NetworkDealer.FileTransferRequestReceivedHandler(ntwkDealer_FileTransferRequestReceived);
            ntwkDealer.VerifyKeyRequest += new Pbdms.NetworkDealer.NetworkDealer.VerifyKeyRequestHandler(ntwkDealer_VerifyKeyRequest);
            ntwkDealer.VerifyKeyResponse += new Pbdms.NetworkDealer.NetworkDealer.VerifyKeyResponseHandler(ntwkDealer_VerifyKeyResponse);
            this.friendsControl = friendsControl;
            this.publicKeyControl = publicKeyControl;
            this.keyControl = secretKeyControl;

        }


        

       

    

     
   

     

       

    

       

     

        
        
   


        
        /// <summary>
        /// handler of the SearchResultReceived event triggered from network dealer component
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void ntwkDealer_searchResultReceived(object sender, Pbdms.NetworkDealer.EventArgs.SearchResultReceivedEventArgs e)
        {
            //pass the result back to the method which has been assigned to handle the result            
            processSearchResultReceived(sender, e);
            
        }

        /// <summary>
        /// handler of the SearchRequestReceived event triggered from network dealer component
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void ntwkDealer_SearchRequestReceived(object sender, Pbdms.NetworkDealer.EventArgs.SearchRequestReceivedEventArgs e)
        {
            if (e.SearchRequestObj.curHop <= MAX_HOPS)
            {
               
                                //perform searching and get result
                Common.DataObject.SearchObj searchObj = e.SearchRequestObj.searchObj;
                searchObj.SearchSource = Common.DataObject.SearchObj.SOURCE_LOCAL_ONLY;
                System.Collections.ArrayList arlResult = Search.SearchProcessor.create(searchObj, false, this).getResult();
                //send the result
                if (arlResult != null && arlResult.Count > 0)
                {
                    foreach (Common.DataObject.ResultItem resItem in arlResult)
                    {
                        String reviewFile = resItem.CommentObj.ReviewFile;
                        resItem.CommentObj.ReviewFile = reviewFile.Substring(reviewFile.LastIndexOf("\\") + 1);
                        resItem.SourceOfItem = Common.DataObject.ResultItem.ITEM_SOURCE_NETWORK;
                        ArrayList temp = new ArrayList();
                        temp.Add(resItem);
                        SearchResult sr = new SearchResult(NetworkDealer.NetworkDealer.ipAddMyComputer.ToString(), e.SearchRequestObj.requestId, temp);
                        System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(typeof(SearchResult));
                        System.IO.StringWriter str = new System.IO.StringWriter();
                        serializer.Serialize(str, sr);
                        AuthTcpClient authClient = new AuthTcpClient(e.SearchRequestObj.ipAddSource, NetworkDealer.NetworkDealer.LISTEN_PORT, null, keyControl, publicKeyControl, friendsControl);
                        authClient.Connect();
                        NetworkMessage message = new NetworkMessage();
                        message.TypeOfMessage = NetworkDealer.NetworkDealer.SEARCH_RESULT;
                        message.MessageContent = str.ToString();
                        authClient.sendMessage(message);

                    }
                }




                System.Collections.ArrayList arlBuddies =friendsControl.getAuthenticatedFriendList();
                if (arlBuddies != null && arlBuddies.Count > 0)
                {
                    foreach (Common.DataObject.Buddy bdd in arlBuddies)
                    {

                        if (!bdd.IpAdd.Equals(e.SearchRequestObj.ipAddSource.ToString()))
                        {
                            if ((e.SearchRequestObj.ipAddForwarder == null
                                || (e.SearchRequestObj.ipAddForwarder != null && !bdd.IpAdd.Equals(e.SearchRequestObj.ipAddForwarder.ToString()))
                                ) && !bdd.IpAdd.Equals(NetworkDealer.NetworkDealer.ipAddMyComputer.ToString()))
                            {
                                //ntwkDealer.forwardSearchRequest(e.SearchRequestObj, IPAddress.Parse(bdd.IpAdd),keyControl.readSecretKey(),keyControl.readPrivateKey());


                                e.SearchRequestObj.curHop++;
                                e.SearchRequestObj.ipAddForwarder = NetworkDealer.NetworkDealer.ipAddMyComputer.ToString();

                                System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(e.SearchRequestObj.GetType());
                                System.IO.StringWriter str = new System.IO.StringWriter();
                                serializer.Serialize(str, e.SearchRequestObj);

               
                             
                            }
                        }

                    }
                }
            }
            
           
            
        }
        
     
        
        public void startListening()
        {
            ntwkDealer.startListening();
        }
        /// <summary>
        /// stop listening on the network
        /// </summary>
        public void stopListening()
        {
            ntwkDealer.stopListening();
        }

        

        #region new source code
        /// <summary>
        /// download request handler
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="message"></param>
        void ntwkDealer_DownloadRequest(object sender, NetworkMessage message)
        {
            try
            {
                if (DownloadRequest != null)
                {
                    DownloadRequest(sender, message);
                }
            }
            catch
            {

            }
        }

        public Boolean pollingTrustedPeer(IPAddress ipAdd, string friendKeyID)
        {
            AuthTcpClient authClient = new AuthTcpClient(ipAdd.ToString(),NetworkDealer.NetworkDealer.LISTEN_PORT, friendKeyID, keyControl, publicKeyControl, friendsControl);
            try
            {
                authClient.Connect();
                Boolean authenticated = authClient.IsAuthenticated();
                return authenticated;

            }
            catch
            {
                return false;
            }
        }
        public void sendKeyToBeVerifyRequest(string keyid)
        {
       
            System.Collections.ArrayList arlBuddies = friendsControl.getAuthenticatedFriendList();

            PgpPublicKey key = publicKeyControl.getPublicKey(keyid);
            foreach (Common.DataObject.Buddy buddy in arlBuddies)
            {

                KeyVerifyRequest req = new KeyVerifyRequest();
                req.curHop = 0;
                req.ipAddForwarder = null;
                req.ipAddSource = NetworkDealer.NetworkDealer.ipAddMyComputer.ToString();
                req.link = "You->" + buddy.Name;
                req.keyData = key.GetEncoded();
                req.keyToBeVerifyID = keyid;
                req.requestId = ntwkDealer.getRequestId();
                System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(typeof(KeyVerifyRequest));
                System.IO.StringWriter str = new System.IO.StringWriter();
                serializer.Serialize(str, req);
                NetworkMessage message = new NetworkMessage();
                message.TypeOfMessage = NetworkDealer.NetworkDealer.VERIFYKEY_REQUEST;
                message.MessageContent = str.ToString();
                AuthTcpClient authTcpClient = new AuthTcpClient(buddy.IpAdd, NetworkDealer.NetworkDealer.LISTEN_PORT, buddy.KeyID, keyControl, publicKeyControl, friendsControl);
                authTcpClient.Connect();
                //send to trusted peers
                if (authTcpClient.IsAuthenticated() == true)
                {
                    authTcpClient.sendMessage(message);
                }
                authTcpClient.Close();
            }
        }
        void ntwkDealer_VerifyKeyRequest(object sender, NetworkMessage message)
        {

            System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(typeof(KeyVerifyRequest));
            //deserialize
            System.IO.StringReader strReader = new System.IO.StringReader(message.MessageContent);
            KeyVerifyRequest req = (KeyVerifyRequest)serializer.Deserialize(strReader);

            trust.trustProcessor trustProcessor = new Pbdms.BusinessLogic.trust.trustProcessor(req, friendsControl, keyControl, this, publicKeyControl);
            trustProcessor.processVerifySignature();
        }
        /// <summary>
        /// forward the verify key request
        /// </summary>
        /// <param name="buddy"></param>
        /// <param name="req"></param>
        public void forward_verifykeyRequest(Common.DataObject.Buddy buddy, KeyVerifyRequest req)
        {
            AuthTcpClient authTcpClient = new AuthTcpClient(buddy.IpAdd, NetworkDealer.NetworkDealer.LISTEN_PORT, buddy.KeyID,keyControl, publicKeyControl, friendsControl);
            try
            {
                authTcpClient.Connect();
                if (authTcpClient.IsAuthenticated() == false)
                {
                    throw new Exception();
                }
              
                System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(typeof(KeyVerifyRequest));
                System.IO.StringWriter str = new System.IO.StringWriter();
                serializer.Serialize(str, req);
                NetworkMessage message = new NetworkMessage();
                message.TypeOfMessage = NetworkDealer.NetworkDealer.VERIFYKEY_REQUEST;
                message.MessageContent = str.ToString();
                authTcpClient.sendMessage(message);
            }
            catch
            {

            }
            finally
            {
                if (authTcpClient != null)
                {
                    authTcpClient.Close();
                }
            }
        }
        /// <summary>
        /// send key verification request
        /// </summary>
        /// <param name="IPdestination"></param>
        /// <param name="response"></param>
        public void sendKeyToBeVerifyResponse(string IPdestination, KeyVerifyResponse response)
        {
            AuthTcpClient authTcpClient = new AuthTcpClient(IPdestination, NetworkDealer.NetworkDealer.LISTEN_PORT, null, keyControl, publicKeyControl, friendsControl);
            try
            {
                authTcpClient.Connect();
                System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(typeof(KeyVerifyResponse));
                System.IO.StringWriter str = new System.IO.StringWriter();
                serializer.Serialize(str, response);
                NetworkMessage message = new NetworkMessage();
                message.TypeOfMessage = NetworkDealer.NetworkDealer.VERIFYKEY_RESPONSE;
                message.MessageContent = str.ToString();
                authTcpClient.sendMessage(message);
            }
            catch
            {
            }
            finally
            {
                authTcpClient.Close();
            }
        }
        /// <summary>
        /// key verification received
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="message"></param>
        void ntwkDealer_VerifyKeyResponse(object sender, NetworkMessage message)
        {
            if (VerifyKeyResponse != null)
            {
                VerifyKeyResponse(sender, message);
            }
        }

        /// <summary>
        /// feed request received
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="message"></param>
        void ntwkDealer_FeedRequestReceived(object sender, NetworkMessage message)
        {
            ///ssss
            AuthTcpClient authClient = (AuthTcpClient)sender;
            string keyID = message.SenderKeyID;
            RSSProcessor.RssProcessor rssProcessor = new Pbdms.BusinessLogic.RSSProcessor.RssProcessor();
            NetworkMessage replyMessage = new NetworkMessage();
            replyMessage.TypeOfMessage = NetworkDealer.NetworkDealer.FEED_RESPONSE;
            Rss.RssChannel rssChannel = rssProcessor.getsFeed();
            MemoryStream outputMemory = new MemoryStream();
            try
            {         
                BinaryFormatter formatter = new BinaryFormatter();               
                formatter.Serialize(outputMemory, rssChannel);
                outputMemory.Seek(0, SeekOrigin.Begin);
                replyMessage.MessageContent = outputMemory.Length.ToString();
                authClient.sendMessage(replyMessage);             ;
                long totalSize = outputMemory.Length;
                long uploaded = 0;
                byte[] buffed = null;
                while (uploaded < totalSize)
                {
                    int length = 1024;
                    //measure the length
                    if (uploaded + 1024 > totalSize)
                    {
                        length = (int)(totalSize - uploaded);
                    }
                    buffed = new byte[length];
                        //Read from the File (len contains the number of bytes read)
                    int numBytesRead = 0;
                    while (length != numBytesRead)
                    {
                        // Read may return anything from 0 to numBytesToRead.
                        int n = outputMemory.Read(buffed, numBytesRead, buffed.Length);
                        numBytesRead += n;
                    }
                    authClient.sendBytes(buffed);
                    uploaded += length;
                }
            }                
            catch
            {

            }
            finally
            {
                //close the memory
                if (outputMemory != null)
                {
                    outputMemory.Close();
                }
                authClient.Close();
            }
        }
        /// <summary>
        /// view an item request
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="message"></param>
        void ntwkDealer_ViewAnItemRequest(object sender, NetworkMessage message)
        {
            
          
                Common.DataObject.ResultItem reqItem = new Pbdms.Common.DataObject.ResultItem();
                reqItem.ItemObj = BusinessLogic.DataController.ItemController.create().getItemFromDB(message.MessageContent);
                ///if the item is not to be shared
                if (reqItem.ItemObj.BPrivate == true)
                {
                    throw new Exception();
                }
                reqItem.FileInfoObj = BusinessLogic.DataController.FileInfoController.create().getFileInfoFromDB(message.MessageContent);
                
                reqItem.CommentObj = BusinessLogic.DataController.CommentController.create().getCommentFromDB(message.MessageContent);
                ntwkDealer.sendViewItemResponse(sender, reqItem);
          
          
            
        }
        void ntwkDealer_InstantMessageReceived(object sender, NetworkMessage message)
        {
            if (InstantMessageReceived != null)
            {
                InstantMessageReceived(sender, message);
            }
            
        }
        
        void ntwkDealer_FileTransferRequestReceived(object sender, NetworkMessage message)
        {
            if (FileTransferRequestReceived != null)
            {
                FileTransferRequestReceived(sender, message);
            }
        }
        public void sendBrowseRequest(String strIpAdd)
        {
            IPAddress ipAdd = IPAddress.Parse(strIpAdd);

            ntwkDealer.sendBrowseRequest(ipAdd);

        }
        void ntwkDealer_browseRequestReceived(object sender, NetworkMessage message)
        {

            //send list of result items:
            System.Collections.ArrayList arlResultItems = new System.Collections.ArrayList();
            System.Collections.ArrayList arlItemIds = DataController.ItemController.create().getListOfItemIDs();

            if (arlItemIds != null && arlItemIds.Count > 0)
            {
                for (int i = 0; i < arlItemIds.Count; i++)
                {
                    Common.DataObject.ResultItem r = new Pbdms.Common.DataObject.ResultItem();
                    r.ItemObj = DataController.ItemController.create().getItemFromDB((String)(arlItemIds[i]));
                    if (r.ItemObj.BPrivate == false)
                    {
                        Common.DataObject.Comment commentObj = DataController.CommentController.create().getCommentFromDB((String)(arlItemIds[i]));


                        commentObj.ReviewFile = commentObj.ReviewFile.Substring(commentObj.ReviewFile.LastIndexOf("\\") + 1);

                        r.CommentObj = commentObj;
                        Common.DataObject.FileInfo fileInfoObj = DataController.FileInfoController.create().getFileInfoFromDB((String)(arlItemIds[i]));
                        fileInfoObj.Location = NetworkDealer.NetworkDealer.ipAddMyComputer.ToString();
                        r.FileInfoObj = fileInfoObj;
                        r.SourceOfItem = Common.DataObject.ResultItem.ITEM_SOURCE_NETWORK;
                        arlResultItems.Add(r);
                    }
                }



            }
            ntwkDealer.sendBrowseResultItems(sender, arlResultItems);

        }
        void ntwkDealer_browseResultBuddyReceived(object sender, SearchResult searchObj)
        {
            if (BrowseResultRequestReceived != null)
            {
                BrowseResultRequestReceived(sender,(ResultItem)searchObj.arlResult[0]);
            }
        }

        #endregion
    
    }
}
